// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_entity_model.h"
#include "mk_engine.h"
#include "mk_render_device.h"
#include "mk_error.h"
#include "mk_bone.h"
#include "mk_math.h"
#include "mk_model.h"
#include "mk_hardware_buffer.h"
#include "mk_render_input.h"
#include "mk_texture.h"
#include "mk_shader.h"
#include "mk_resource_manager.h"
#include "mk_scene_node.h"
#include "mk_painter.h"
#include "mk_camera.h"
#include "mk_memory_block.h"
#include "mk_file_system.h"
#include "mk_material.h"
#include "mk_scene.h"
#include "mk_skeleton_data.h"

namespace MK3DE
{
	BOOL CEntityModel::_s_softwareSkin = FALSE;

	CEntityModel::CEntityModel(CScene* pScene)
		: CEntity(pScene)
	{
		_model = NULL;
		_hasSkin = FALSE;
		_vbPosition = NULL;
		_vbNormal = NULL;
		_vbUV = NULL;
		_vbBoneIndex = NULL;
		_vbWeight = NULL;
		_ibIndex = NULL;
		_renderInput = NULL;
		_renderInputShadow = NULL;
		_shader = NULL;
	}

	CEntityModel::~CEntityModel()
	{
		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		SAFE_RELEASE(_ibIndex);
		SAFE_RELEASE(_vbPosition);
		SAFE_RELEASE(_vbNormal);
		SAFE_RELEASE(_vbUV);
		SAFE_RELEASE(_vbBoneIndex);
		SAFE_RELEASE(_vbWeight);
		rd->DestroyRenderInput(_renderInput);
		rd->DestroyRenderInput(_renderInputShadow);
		for (int i = 0; i < (int)_vecMaterial.size(); ++i)
			SAFE_DELETE(_vecMaterial[i]);
		_vecMaterial.clear();
		SAFE_RELEASE(_shader);
		SAFE_DELETE(_model);
	}

	void CEntityModel::SetAnimationInterval(float time)
	{
		_model->SetAnimationInterval(time);
	}

	BOOL CEntityModel::__LoadModel(const char* fileName)
	{
		BOOL ret = FALSE;
		int result = 1;
		TiXmlDocument* pXML = NULL;
		TiXmlElement* node = NULL;
		TiXmlElement* root = NULL;
		const char* shaderFileName = NULL;
		CMemoryBlock block;

		ret = GetEngine()->GetFileSystem()->ReadFile(fileName, &block);
		MK_PROCESS_ERROR(ret);

		pXML = new TiXmlDocument();
		MK_PROCESS_ERROR(pXML);

		pXML->SetCondenseWhiteSpace(false);
		pXML->Parse((const char*)block.GetBuffer());
		MK_PROCESS_ERROR(pXML->Error() == false);

		root = pXML->RootElement();
		MK_PROCESS_ERROR(root);

		result = strcmp(root->Value(), "Model");
		MK_PROCESS_ERROR(result == 0);

		shaderFileName = root->Attribute("ShaderFile");
		MK_PROCESS_ERROR(shaderFileName);

		ret = GetEngine()->GetResourceManager()->GetShaderFromFile(shaderFileName, _shader);
		MK_PROCESS_ERROR(ret);

		node = root->FirstChildElement("Animation");

		while (node)
		{
			const char* skeletonFile = node->Attribute("AnimationFile");
			MK_PROCESS_ERROR(skeletonFile);

			ret = _model->AddSkeleton(skeletonFile);
			MK_PROCESS_ERROR(ret);

			node = node->NextSiblingElement("Animation");
		}

		node = root->FirstChildElement("Material");
		MK_PROCESS_ERROR(node);
		while (node)
		{
			const char* materialFile = node->Attribute("MaterialFile");
			MK_PROCESS_ERROR(materialFile);

			ret = __LoadMaterial(materialFile);
			MK_PROCESS_ERROR(ret);

			node = node->NextSiblingElement("Material");
		}

		node = root->FirstChildElement("SubMesh");
		MK_PROCESS_ERROR(node);
		while (node)
		{
			int mtlIdx = -1;
			const char* subMeshFile = node->Attribute("MeshFile");
			MK_PROCESS_ERROR(subMeshFile);

			result = node->QueryIntAttribute("MaterialIdx", &mtlIdx);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);
			MK_PROCESS_ERROR(mtlIdx < (int)_vecMaterial.size());			

			ret = _model->AddSubMesh(subMeshFile, mtlIdx);
			MK_PROCESS_ERROR(ret);

			node = node->NextSiblingElement("SubMesh");
		}

		SortSubMeshByMaterialIdx();

		_model->CalculateSkeletonBaseTranformation();

		ret = TRUE;
Exit0:
		SAFE_DELETE(pXML);

		return ret;
	}

	BOOL CEntityModel::__LoadMaterial(const char* materialFile)
	{
		BOOL ret = FALSE;
		CMaterial* mtl = new CMaterial();
		MK_PROCESS_ERROR(mtl);

		ret = mtl->CreateAllProperties();
		MK_PROCESS_ERROR(ret);

		ret = mtl->LoadFromFile(materialFile);
		MK_PROCESS_ERROR(ret);

		_vecMaterial.push_back(mtl);

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CEntityModel::LoadFromFile(const char* modelFile)
	{
		BOOL ret = FALSE;

		_model = new CModel;
		MK_PROCESS_ERROR(_model);

		ret = __LoadModel(modelFile);
		MK_PROCESS_ERROR(ret);

		int vertexCount = _model->GetVertexCount();
		int faceCount = _model->GetFaceCount();

		_hasSkin = FALSE;
		for (int i = 0; i < _model->GetSubMeshCount(); ++i)
			if (_model->GetSubMeshByIdx(i)->IsSkinMesh())
				_hasSkin = TRUE;

		BOOL gpu = _hasSkin && (!_s_softwareSkin) ? TRUE : FALSE;

		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		CResourceManager* rm = GetEngine()->GetResourceManager();

		HARDWARE_BUFFER_USAGE usage = (HARDWARE_BUFFER_USAGE)(HBU_DYNAMIC | HBU_WRITE_ONLY);
		LOCK_OPTION lockOp = LO_DISCARD;
		if (gpu)
		{
			usage = HBU_STATIC;
			lockOp = LO_READ_WRITE;
		}

		ret = rm->GetVertexBuffer(sizeof(D3DXVECTOR3), vertexCount, usage, _vbPosition);
		MK_PROCESS_ERROR(ret);

		ret = rm->GetVertexBuffer(sizeof(D3DXVECTOR3), vertexCount, usage, _vbNormal);
		MK_PROCESS_ERROR(ret);

		ret = rm->GetVertexBuffer(sizeof(D3DXVECTOR2), vertexCount, HBU_STATIC, _vbUV);
		MK_PROCESS_ERROR(ret);

		if (gpu)
		{
			ret = rm->GetVertexBuffer(sizeof(D3DXVECTOR4), vertexCount, HBU_STATIC, _vbBoneIndex);
			MK_PROCESS_ERROR(ret);

			ret = rm->GetVertexBuffer(sizeof(D3DXVECTOR4), vertexCount, HBU_STATIC, _vbWeight);
			MK_PROCESS_ERROR(ret);    
		}

		ret = rm->GetIndexBuffer(faceCount * 3, IT_32BIT, HBU_STATIC, _ibIndex);
		MK_PROCESS_ERROR(ret);

		if (gpu)
		{
			RenderInputElemDesc rie[] = { 
				RenderInputElemDesc(SU_POSITION, 0, 0, 0),
				RenderInputElemDesc(SU_NORMAL, 0, 0, 1),
				RenderInputElemDesc(SU_TEXCOORD, 0, 0, 2),
				RenderInputElemDesc(SU_BLEND_INDICES, 1, 0, 3),
				RenderInputElemDesc(SU_BLEND_WEIGHT, 2, 0, 4),
			};

			ret = rd->CreateRenderInput(rie, 5, _renderInput);
			MK_PROCESS_ERROR(ret);

			ret = _renderInput->SetVertexBuffer(0, _vbPosition);
			MK_PROCESS_ERROR(ret);

			ret = _renderInput->SetVertexBuffer(1, _vbNormal);
			MK_PROCESS_ERROR(ret);

			ret = _renderInput->SetVertexBuffer(2, _vbUV);
			MK_PROCESS_ERROR(ret);

			ret = _renderInput->SetVertexBuffer(3, _vbBoneIndex);
			MK_PROCESS_ERROR(ret);

			ret = _renderInput->SetVertexBuffer(4, _vbWeight);
			MK_PROCESS_ERROR(ret);

			RenderInputElemDesc rie_shadow[] = { 
				RenderInputElemDesc(SU_POSITION, 0, 0, 0),
				RenderInputElemDesc(SU_BLEND_INDICES, 0, 0, 1),
				RenderInputElemDesc(SU_BLEND_WEIGHT, 1, 0, 2),
			};

			ret = rd->CreateRenderInput(rie_shadow, 3, _renderInputShadow);
			MK_PROCESS_ERROR(ret);

			ret = _renderInputShadow->SetVertexBuffer(0, _vbPosition);
			MK_PROCESS_ERROR(ret);

			ret = _renderInputShadow->SetVertexBuffer(1, _vbBoneIndex);
			MK_PROCESS_ERROR(ret);

			ret = _renderInputShadow->SetVertexBuffer(2, _vbWeight);
			MK_PROCESS_ERROR(ret);
		}
		else
		{
			RenderInputElemDesc rie[] = { 
				RenderInputElemDesc(SU_POSITION, 0, 0, 0),
				RenderInputElemDesc(SU_NORMAL, 0, 0, 1),
				RenderInputElemDesc(SU_TEXCOORD, 0, 0, 2),
			};

			RenderInputElemDesc rieShadow[] = { 
				RenderInputElemDesc(SU_POSITION, 0, 0, 0),
				RenderInputElemDesc(SU_TEXCOORD, 0, 0, 1),
			};

			ret = rd->CreateRenderInput(rie, 3, _renderInput);
			MK_PROCESS_ERROR(ret);

			ret = _renderInput->SetVertexBuffer(0, _vbPosition);
			MK_PROCESS_ERROR(ret);

			ret = _renderInput->SetVertexBuffer(1, _vbNormal);
			MK_PROCESS_ERROR(ret);

			ret = _renderInput->SetVertexBuffer(2, _vbUV);
			MK_PROCESS_ERROR(ret);

			ret = rd->CreateRenderInput(rieShadow, 2, _renderInputShadow);
			MK_PROCESS_ERROR(ret);

			ret = _renderInputShadow->SetVertexBuffer(0, _vbPosition);
			MK_PROCESS_ERROR(ret);

			ret = _renderInputShadow->SetVertexBuffer(1, _vbUV);
			MK_PROCESS_ERROR(ret);
		}

		_renderInput->SetIndexBuffer(_ibIndex);
		_renderInputShadow->SetIndexBuffer(_ibIndex);

		if (gpu)
		{
			D3DXVECTOR4* boneIdx;
			D3DXVECTOR4* boneWeight;
			ret = _vbBoneIndex->Lock((void*&)boneIdx, 0, 0, lockOp);
			MK_PROCESS_ERROR(ret);

			ret = _vbWeight->Lock((void*&)boneWeight, 0, 0, lockOp);
			MK_PROCESS_ERROR(ret);

			ret = _model->FillSkinBuffer(boneIdx, boneWeight, vertexCount);
			MK_PROCESS_ERROR(ret);

			ret = _vbBoneIndex->Unlock();
			MK_PROCESS_ERROR(ret);

			ret = _vbWeight->Unlock();
			MK_PROCESS_ERROR(ret);
		}

		ret = UpdateVBFaceIndex();
		MK_PROCESS_ERROR(ret);

		ret = UpdateVBPosNormal(lockOp);
		MK_PROCESS_ERROR(ret);

		ret = UpdateVBTexCoord();
		MK_PROCESS_ERROR(ret);

		_fileName = modelFile;

		__UpdateLocalAABB();

		return TRUE;

Exit0:
		return FALSE;
	}

	void CEntityModel::__UpdateLocalAABB()
	{
		_localAABB.Clear();
		int meshCount = _model->GetSubMeshCount();
		for (int i = 0; i < meshCount; ++i)
		{
			CMesh* mesh = _model->GetSubMeshByIdx(i);
			if (mesh)
			{
				AABB aabb = mesh->GetAABB();
				_localAABB.Merge(&aabb);
			}
		}

		UpdateWorldAABB();
	}

	void CEntityModel::Update(DWORD dt, BOOL bChanged)
	{
		if (!_sceneNode)
			return;

		_model->UpdateSkeleton(dt, bChanged);

		CEntity::Update(dt, bChanged);
	}

	BOOL CEntityModel::UseAnimation(DWORD idx, float time)
	{
		return _model->UseAnimation(idx, time);
	}

	void CEntityModel::_AttachSceneNode(CSceneNode* pNode)
	{
		CEntity::_AttachSceneNode(pNode);
		CSkeleton* skeleton = _model->GetCurrentAnimation();
		if (!skeleton)
			return;

		CBone* bone = skeleton->GetBone(0);
		bone->SetParent(pNode);
	}

	BOOL CEntityModel::__RenderSkeleton()
	{
		CPainter* painter = GetEngine()->GetPainter();

		CSkeleton* skeleton = _model->GetCurrentAnimation();
		DWORD boneCount = skeleton->GetSkeletonData()->GetBoneCount();
		for (DWORD i = 0; i < boneCount; ++i)
		{
			CBone* pBone = skeleton->GetBone(i);
			CBone* pParent = skeleton->GetBone(pBone->GetParentIdx());
			if (pParent)
			{
				D3DXMATRIX mat = pBone->GetAbsoluteTransformation();
				D3DXMATRIX matParent = pParent->GetAbsoluteTransformation();
				D3DXVECTOR3 v1(mat._41, mat._42, mat._43);
				D3DXVECTOR3 v2(matParent._41, matParent._42, matParent._43);
				painter->DrawLine3D(v1 + D3DXVECTOR3(0, 0, 0), v2 + D3DXVECTOR3(0, 0, 0), 0xff00ff00, 0xffffffff);
			}
		}

		return TRUE;
	}

	void CEntityModel::__UpdateBonePalette(CShader* shader)
	{
		CSkeleton* pSkeleton = _model->GetCurrentAnimation();
		if (pSkeleton)
		{
			for (DWORD i = 0; i < pSkeleton->GetSkeletonData()->GetBoneCount(); ++i)
			{
				CBone* bone = pSkeleton->GetBone(i);
				D3DXMATRIX mat = bone->GetLocalTransformFromBase();
				char name[64];
				sprintf(name, "%s[%d]", SCN_MAT_BONE_PALETTE, i);
				shader->SetMatrix(name, &mat);
			}
		}
	}

	BOOL CEntityModel::UpdateVBFaceIndex()
	{
		BOOL ret = FALSE;

		int faceCount = _model->GetFaceCount();

		DWORD* pi;
		ret = _ibIndex->Lock((void*&)pi, 0, 0, LO_READ_WRITE);
		MK_PROCESS_ERROR(ret);

		_model->FillFaceBuffer((CMesh::Face*)pi, faceCount);

		ret = _ibIndex->Unlock();
		MK_PROCESS_ERROR(ret);

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CEntityModel::UpdateVBPosNormal(LOCK_OPTION lockOp)
	{
		BOOL ret = FALSE;

		CMesh::Position* pos;
		CMesh::Normal* normal;
		ret = _vbPosition->Lock((void*&)pos, 0, 0, lockOp);
		MK_PROCESS_ERROR(ret);

		ret = _vbNormal->Lock((void*&)normal, 0, 0, lockOp);
		MK_PROCESS_ERROR(ret);

		_model->FillVertexBuffer(pos, normal, _model->GetVertexCount());

		ret = _vbPosition->Unlock();
		MK_PROCESS_ERROR(ret);

		ret = _vbNormal->Unlock();
		MK_PROCESS_ERROR(ret);

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CEntityModel::UpdateVBTexCoord()
	{
		BOOL ret = FALSE;

		int vertexCount = _model->GetVertexCount();

		float* puv;
		ret = _vbUV->Lock((void*&)puv, 0, 0, LO_READ_WRITE);
		MK_PROCESS_ERROR(ret);

		_model->FillUVBuffer((CMesh::TextureCoord*)puv, vertexCount);

		ret = _vbUV->Unlock();
		MK_PROCESS_ERROR(ret);

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CEntityModel::__UpdateVertexBuffer()
	{
		BOOL ret = FALSE;
		if (_hasSkin && (!_s_softwareSkin))
		{
			__UpdateBonePalette(_shader);
		}
		else
		{
			DWORD vbState = _model->PrepareUpdateVertexData();
			if (vbState | CModel::VB_DIRTY_FACE_INDEX)
			{
				ret = UpdateVBFaceIndex();
				MK_PROCESS_ERROR(ret);
			}

			if (vbState | CModel::VB_DIRTY_POS_NORMAL)
			{
				ret = UpdateVBPosNormal(LO_DISCARD);
				MK_PROCESS_ERROR(ret);
			}

			if (vbState | CModel::VB_DIRTY_TEXCOORD)
			{
				ret = UpdateVBTexCoord();
				MK_PROCESS_ERROR(ret);
			}
		}

		ret = TRUE;

Exit0:
		return ret;
	}

	void CEntityModel::__BindRenderInput()
	{
		CEngine* engine = GetEngine();
		CCamera* camera = engine->GetCurrentCamera();
		CRenderDevice* rd = engine->GetRenderDevice();
		rd->BindRenderInput(_renderInput);

		D3DXMATRIX& matWorld = _sceneNode->GetAbsoluteTransformation();
		D3DXMATRIX matWorldViewProj = matWorld * camera->GetViewProjMatrix();
		D3DXMATRIX matWorldView = matWorld * camera->GetViewMatrix();
		D3DXMATRIX matLightWorldViewProj = matWorld * _scene->GetShadowMapCalculator()->GetMatrixLightViewProj();
		_shader->SetMatrix(SCN_MAT_WORLD_VIEW_PROJ, &matWorldViewProj);
		_shader->SetMatrix(SCN_MAT_WORLD_VIEW, &matWorldView);
		_shader->SetMatrix(SCN_MAT_WORLD, &matWorld);
		_shader->SetMatrix(SCN_MAT_LIGHT_WORLD_VIEW_PROJ, &matLightWorldViewProj);
		_shader->SetMatrix(SCN_MAT_SHADOW_MAT_WRAP, &_scene->GetShadowMapCalculator()->GetMatrixTrapezoidal());
	}

	void CEntityModel::__RenderSubMesh(int idx, std::string& baseTechName, int vertexCount, int& indexBufferOffset, int& currentSubMeshIdx)
	{
		BOOL ret = FALSE;

		CEngine* engine = GetEngine();
		CRenderDevice* rd = engine->GetRenderDevice();

		int faceCount = 0;
		for (int j = 0; j < _vecGroupSubMeshCount[idx]; ++j)
		{
			CMesh* mesh = _model->GetSubMeshByIdx(currentSubMeshIdx++);
			faceCount += mesh->GetFaceCount();
		}

		if (faceCount == 0)
			return;

		CMaterial* material = _vecMaterial[idx];
		material->SetMaterial(_scene, _shader, FALSE);
		std::string techName = baseTechName + material->GenerateTechniquePostfix(_bShadowRecv);
		DWORD passNum;
		ret = _shader->TechniqueBegin(techName.c_str(), passNum);
		MK_PROCESS_ERROR(ret);

		for (DWORD pass = 0; pass < passNum; ++pass)
		{
			if (_shader->PassBegin(pass))
			{
				BOOL ret = rd->DrawPrimitive(PT_TRIANGLELIST, 0, 0, vertexCount, indexBufferOffset, faceCount);
				MK_PROCESS_ERROR(ret);

				_shader->PassEnd();
			}
		}

		_shader->TechniqueEnd();
		indexBufferOffset += faceCount * 3;

		material->RestoreMaterial();

Exit0:
		return;
	}

	void CEntityModel::Render()
	{
		if (!_sceneNode || !_renderInput || !_bVisible)
			return;

		BOOL ret = FALSE;

		std::string baseTechName(TECH_MODEL_NORMAL);
		if (_hasSkin && (!_s_softwareSkin))
			baseTechName = TECH_MODEL_SKIN;

		ret = __UpdateVertexBuffer();
		MK_PROCESS_ERROR(ret);

		__BindRenderInput();

		if (_bShadowRecv)
			_shader->SetTexture(SCN_SHADOW_MAP_TEXTURE, _scene->GetShadowMapTexture());

		int vertexCount = _model->GetVertexCount();
		int indexBufferOffset = 0;
		int currentSubMeshIdx = 0;
		ASSERT(_vecGroupSubMeshCount.size() == _vecMaterial.size());
		for (int i = 0; i < (int)_vecGroupSubMeshCount.size(); ++i)
			__RenderSubMesh(i, baseTechName, vertexCount, indexBufferOffset, currentSubMeshIdx);

Exit0:
		return;
	}

	void CEntityModel::__BindRenderInputShadow(const D3DXMATRIX& matViewProj, const D3DXMATRIX& matWrap)
	{
		CEngine* engine = GetEngine();
		CRenderDevice* rd = engine->GetRenderDevice();
		rd->BindRenderInput(_renderInputShadow);

		D3DXMATRIX matWorldViewProj = _sceneNode->GetAbsoluteTransformation() * matViewProj;
		_shader->SetMatrix(SCN_MAT_WORLD_VIEW_PROJ, &matWorldViewProj);
		_shader->SetMatrix(SCN_MAT_SHADOW_MAT_WRAP, &matWrap);
	}

	void CEntityModel::__RenderSubMeshShadow(int idx, std::string& baseTechName, int vertexCount, int& indexBufferOffset, int& currentSubMeshIdx)
	{
		BOOL ret = FALSE;

		CEngine* engine = GetEngine();
		CRenderDevice* rd = engine->GetRenderDevice();

		int faceCount = 0;
		for (int j = 0; j < _vecGroupSubMeshCount[idx]; ++j)
		{
			CMesh* mesh = _model->GetSubMeshByIdx(currentSubMeshIdx++);
			faceCount += mesh->GetFaceCount();
		}

		if (faceCount == 0)
			return;

		CMaterial* material = _vecMaterial[idx];
		material->SetMaterial(_scene, _shader, TRUE);

		std::string techName = baseTechName;
		DWORD passNum;
		ret = _shader->TechniqueBegin(techName.c_str(), passNum);
		MK_PROCESS_ERROR(ret);

		for (DWORD pass = 0; pass < passNum; ++pass)
		{
			if (_shader->PassBegin(pass))
			{
				BOOL ret = rd->DrawPrimitive(PT_TRIANGLELIST, 0, 0, vertexCount, indexBufferOffset, faceCount);
				MK_PROCESS_ERROR(ret);

				_shader->PassEnd();
			}
		}

		_shader->TechniqueEnd();
		indexBufferOffset += faceCount * 3;

		material->RestoreMaterial();

Exit0:
		return;
	}

	void CEntityModel::RenderShadow(const D3DXMATRIX& matViewProj, const D3DXMATRIX& matWrap)
	{
		if (!_bShadowCast || !_sceneNode || !_bVisible || !_shader)
			return;

		BOOL ret = FALSE;

		CEngine* engine = GetEngine();
		CRenderDevice* rd = engine->GetRenderDevice();

		std::string baseTechName(TECH_MODEL_NORMAL_CAST_SHADOW);
		if (_hasSkin && (!_s_softwareSkin))
			baseTechName = TECH_MODEL_SKIN_CAST_SHADOW;

		ret = __UpdateVertexBuffer();
		MK_PROCESS_ERROR(ret);

		__BindRenderInputShadow(matViewProj, matWrap);

		int vertexCount = _model->GetVertexCount();
		int indexBufferOffset = 0;
		int currentSubMeshIdx = 0;
		ASSERT(_vecGroupSubMeshCount.size() == _vecMaterial.size());
		for (int i = 0; i < (int)_vecGroupSubMeshCount.size(); ++i)
			__RenderSubMeshShadow(i, baseTechName, vertexCount, indexBufferOffset, currentSubMeshIdx);

Exit0:
		return;
	}

	BOOL CEntityModel::CollideWithRay(D3DXVECTOR3& pos, float x, float y, float z, D3DXVECTOR3& dir)
	{
		if (!_sceneNode)
			return FALSE;

		return _model->CollideWithRay(_sceneNode->GetAbsoluteTransformation(), pos, x, y, z, dir);
	}

	BOOL CEntityModel::GetMaterialInfo(DWORD idx, /* out */ CMaterial*& material, /* out */ int& groupSubMeshCount)
	{
		if (idx >= (int)_vecMaterial.size())
			return FALSE;

		material = _vecMaterial[idx];
		groupSubMeshCount = _vecGroupSubMeshCount[idx];
		return TRUE;
	}

	BOOL CEntityModel::RemoveMaterial(DWORD idx)
	{
		if (idx >= (int)_vecMaterial.size())
			return FALSE;
		_vecMaterial.erase(_vecMaterial.begin() + idx);
		return TRUE;
	}

	void CEntityModel::SortSubMeshByMaterialIdx()
	{
		_model->SortSubMeshByMaterialIdx((int)_vecMaterial.size(), _vecGroupSubMeshCount);
	}
}